package com.szboanda.execution;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import com.szboanda.parser.DataBaseType;
import com.szboanda.parser.SqlContext;
import com.szboanda.parser.SqlScriptParserUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Map;
import java.util.List;

/**
 * @author ：zhangsheng
 * @date ：Created in 2020/2/22 9:25
 * @description：sql执行器
 * @modified By：
 * @version: 1.0$
 */
public class SqlExecution {

    /**
     * 日志
     */
    final static Logger logger = LoggerFactory.getLogger(SqlExecution.class);

    /**
     * 单例
     */
    private static volatile SqlExecution ourInstance;

    /**
     *
     * @return
     */
    public static SqlExecution getInstance() {
        if (null == ourInstance) {
            synchronized (SqlExecution.class) {
                if (null == ourInstance) {
                    ourInstance = new SqlExecution();
                }
            }
        }
        return ourInstance;
    }

    /**
     * 私有构造器
     */
    private SqlExecution() {
    }



    /**
     *  查询
     *  ##{where}             context  where="name=#{name} and class=#{}"  name=xx class=ss
     *  where name=#{name}    context  name=
     * @param con 数据库连接，使用之后不会关闭
     * @param sql 带标签的sql脚本
     * @param context 运行参数
     * @return
     */
    public List<Map<String, Object>> select(Connection con, String sql, SqlContext context) {
        logger.info("执行器查询：原始sql={}，context={}", sql, context);
        DataBaseType dataBaseType = this.getType(con);
        context.setDataBaseType(dataBaseType);
        List<Object> sqlArgs = new ArrayList<Object>();
        String selectSql = SqlScriptParserUtil.getInstance().getBindingSql(sql, sqlArgs, context);
        logger.info("执行器查询：解析后sql={}，sqlArgs={}", selectSql, sqlArgs);
        ConnectionWrapper wrapper = new ConnectionWrapper(con, context.getQueryTimeOut());
        String pageSql = SqlScriptParserUtil.getInstance().getQueryLimitSql(selectSql, context.getQueryLimitSize(), dataBaseType);
        logger.info("执行器查询：增加查询上限后sql={}，sqlArgs={}", pageSql, sqlArgs);
        try {
            return wrapper.excuteQuery(pageSql, sqlArgs.toArray());
        } catch (Exception e) {
            logger.error("执行器查询：解析后sql={}，sqlArgs={},\n 异常={}", pageSql, sqlArgs, e);
            throw new SqlExecutionException(e);
        }
    }

    /**
     * 获取数据库类型
     * @param con 数据库连接
     * @return
     */
    private DataBaseType getType(Connection con){
        DataBaseType dataBaseType = DataBaseType.Mysql;
        try{
            DatabaseMetaData md = con.getMetaData();
            String databaseName = md.getDatabaseProductName();
            dataBaseType = DataBaseType.toEnum(databaseName);
        } catch (SQLException e){
            logger.error("获取数据库类型异常，e={}", e);
        } finally {
            logger.info("获取数据库类型为：{}", dataBaseType.name());
            return dataBaseType;
        }
    }


    /**
     * 分页查询
     * @param con 数据库连接，使用完不关闭
     * @param sql 带标签的 sql脚本
     * @param context sql解析、执行参数
     * @return
     */
    public PageInfo<Map<String, Object>> selectPage(Connection con, String sql, SqlContext context) {
        logger.info("执行器分页查询：原始sql={}，context={}", sql, context);
        Page<Map<String,Object>> page = new Page<Map<String,Object>>();
        page.setPageNum(context.getPageNum());
        page.setPageSize(context.getPageSize());
        List<Object> sqlArgs = new LinkedList<Object>();
        //执行列表
        DataBaseType dataBaseType = this.getType(con);
        context.setDataBaseType(dataBaseType);
        SqlScriptParserUtil sqlScriptParserUtil = SqlScriptParserUtil.getInstance();
        String selectSql = sqlScriptParserUtil.getBindingSql(sql, sqlArgs, context);
        String countSql = sqlScriptParserUtil.getCountSql(selectSql, dataBaseType);
        logger.info("执行器分页查询：countSql={}，sqlArgs={}", countSql, sqlArgs);
        ConnectionWrapper wrapper = new ConnectionWrapper(con, context.getQueryTimeOut(), context.isQueryNeedRow());
        try {

            //数量
            Object countOjb = wrapper.executeQuerySingle(countSql, sqlArgs.toArray());
            page.setTotal(this.getCount(countOjb));
            String pageSql = sqlScriptParserUtil.getPageSql(selectSql, page.getPageNum(), page.getPageSize(), dataBaseType);
            logger.info("执行器分页查询：pageSql={}，sqlArgs={}", pageSql, sqlArgs);
            List<Map<String, Object>> resultLst = wrapper.excuteQuery(pageSql, sqlArgs.toArray());
            page.addAll(resultLst);
            return page.toPageInfo();
        } catch (Exception e) {
            logger.error("执行器查询：解析后sql={}，context={},\n 异常={}" , selectSql, context, e);
            throw new SqlExecutionException(e);
        }
    }

    /**
     * 获取long 型 数量
     * @param countOjb
     * @return
     */
    private long getCount(Object countOjb) {
        if (countOjb instanceof Long) {
            long count = (Long)countOjb;
            return count;
        } else if (countOjb instanceof Integer) {
            Integer count = (Integer)countOjb;
            return count.longValue();
        } else if (countOjb instanceof BigInteger) {
            BigInteger count = (BigInteger)countOjb;
            return count.longValue();
        } else if (countOjb instanceof BigDecimal) {
            BigDecimal count = (BigDecimal)countOjb;
            return count.longValue();
        } else {
            Integer count = (Integer)countOjb;
            return count.longValue();
        }
    }


}
